/* Copyright (C) 2010 Luca Piccinelli
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/* 
 * File:   pipe_meta.
 * Author: picci
 *
 * Created on June 3, 2011, 12:37 PM
 */

#include "pipeline/pipe_meta.h"
#include "factory/factories.h"

namespace NAMESPACE{
// ****** PipeStepDescriptor implementation ************************************    
    
PipeStepDescriptor::~PipeStepDescriptor(){}
PipeStepDescriptor::PipeStepDescriptor() : id(),
                                           in_generators(), 
                                           out_generators(), 
                                           formatters(),
                                           criteria() {}
PipeStepDescriptor::PipeStepDescriptor(const PipeStepDescriptor& d) : id(d.id),
                                                                      in_generators(d.in_generators), 
                                                                      out_generators(d.out_generators), 
                                                                      formatters(d.formatters),
                                                                      criteria(d.criteria) {}
const PipeStepDescriptor& PipeStepDescriptor::operator=(const PipeStepDescriptor& d){
    if(this == &d) return *this;
    
    id             = d.id;
    in_generators  = d.in_generators;
    out_generators = d.out_generators;
    formatters     = d.formatters;
    criteria       = d.criteria;
            
    return *this;
}
  
PipeStepDescriptor::PipeStepDescriptor(const std::string& _id) : id(_id),
                                                                 in_generators(), 
                                                                 out_generators(), 
                                                                 formatters(),
                                                                 criteria() {}

const std::string& PipeStepDescriptor::get_id() const{ return id; }
const std::pair<std::string, Configuration>& PipeStepDescriptor::get_in_gen(const IOKey& k) const{
    return in_generators.at(k);
}
const Configuration& PipeStepDescriptor::get_in_conf(const IOKey& k, const std::string& gen) const{
    return in_generators.at(k).second;
}
const std::pair<std::string, Configuration>& PipeStepDescriptor::get_out_gen(const IOKey& k) const{
    return out_generators.at(k);
}
const Configuration& PipeStepDescriptor::get_out_conf(const IOKey& k, const std::string& gen) const{
    return out_generators.at(k).second;
}
const std::map<std::string, Configuration>&  PipeStepDescriptor::get_formatters(const IOKey& k) const{
    return formatters.at(k);
}
const Configuration& PipeStepDescriptor::get_formatter_conf(const IOKey& k, const std::string& form) const{
    return formatters.at(k).at(form);
}
const std::string& PipeStepDescriptor::get_criteria(const IOKey& k) const{
    return criteria.at(k);
}

// *** Setters

PipeStepDescriptor& PipeStepDescriptor::set_id(const std::string& _id){
    id = _id;
}
PipeStepDescriptor& PipeStepDescriptor::set_in_gen(const IOKey& k, const std::pair<std::string, Configuration>& gen){
    in_generators[k] = gen;
}
PipeStepDescriptor& PipeStepDescriptor::set_in_conf(const IOKey& k, const std::string& gen, const Configuration& conf){
    in_generators[k] = std::pair<std::string, Configuration>(gen, conf);
}
PipeStepDescriptor& PipeStepDescriptor::set_out_gen(const IOKey& k, const std::pair<std::string, Configuration>& gen){
    out_generators[k] = gen;
}
PipeStepDescriptor& PipeStepDescriptor::set_out_conf(const IOKey& k, const std::string& gen, const Configuration& conf){
    out_generators[k] = std::pair<std::string, Configuration>(gen, conf);
}
PipeStepDescriptor& PipeStepDescriptor::set_formatters(const IOKey& k, const std::map<std::string, Configuration>& formatter){
    formatters[k] = formatter;
}
PipeStepDescriptor& PipeStepDescriptor::set_formatter_conf(const IOKey& k, const std::string& form, const Configuration& conf){
    formatters[k][form] = conf;
}
PipeStepDescriptor& PipeStepDescriptor::set_criteria(const IOKey& k, const std::string& value){
    criteria[k] = value;
}

const std::map<IOKey, std::string>& PipeStepDescriptor::get_all_criteria() const{ return criteria; }
const std::map< IOKey, std::pair<std::string, Configuration> >& PipeStepDescriptor::get_all_in_generators() const { return in_generators; }
const std::map< IOKey, std::pair<std::string, Configuration> >& PipeStepDescriptor::get_all_out_generators() const { return out_generators; };
const std::map< IOKey, std::map<std::string, Configuration> >&  PipeStepDescriptor::get_all_formatters() const { return formatters; }

// -----------------------------------------------------------------------------/   

// ###### VISITORS #############################################################

// ****** GenericPDSVisitor implementation *************************************
    
void GenericPDSVisitor::get_pipeline(const PipelineDescriptor& d){}
void GenericPDSVisitor::get_single_step(const SingleStepDescriptor& d){
    computation = FactoriesPool<PipeStepComputation>::get_instance()->build(d.get_computation_name());
}

void GenericPDSVisitor::set_pipeline(PipelineDescriptor& d) const{ 
    if(descr_builder) {
        std::vector<PipeStepDescriptor*> descriptors;
        descr_builder->build(descriptors);
        d.set_computation(descriptors); 
    }

}
void GenericPDSVisitor::set_single_step(SingleStepDescriptor& d) const{ d.set_computation(computation_name); }

void GenericPDSVisitor::set_computation_name(const std::string& name){ computation_name = name;}
void GenericPDSVisitor::set_descr_builder(PipeStepDescrBuilder* builder){ descr_builder = builder; }

PipeStepComputation* GenericPDSVisitor::get_computation() const { return computation; }
// -----------------------------------------------------------------------------

// _____________________________________________________________________________

// ****** AbsPipeStepDescrConsumer implementation ******************************
AbsPipeStepDescrConsumer::~AbsPipeStepDescrConsumer(){}
AbsPipeStepDescrConsumer::AbsPipeStepDescrConsumer() : PipeStepDescrConsumer(){}
AbsPipeStepDescrConsumer::AbsPipeStepDescrConsumer(const AbsPipeStepDescrConsumer& c) : PipeStepDescrConsumer(c){}
const AbsPipeStepDescrConsumer& AbsPipeStepDescrConsumer::operator=(const AbsPipeStepDescrConsumer& c){
    if(this == &c) return *this;
    PipeStepDescrConsumer::operator=(c);
    return *this;
}
// -----------------------------------------------------------------------------


// ****** PipelineDescriptor implementation ************************************
PipelineDescriptor::PipelineDescriptor() : PipeStepDescriptor(), computation(), computation_name(){}

PipelineDescriptor::PipelineDescriptor(const PipelineDescriptor& p) : PipeStepDescriptor(p), computation(), computation_name(p.computation_name){    
    std::vector<PipeStepDescriptor*> tmp_computation = computation;
    
    try{
        computation.clear();
        
        for(int i = 0; i < p.computation.size(); i++){ 
            if(p.computation[i]) computation.push_back(p.computation[i]->clone()); 
        }
    }catch(...){
        for(int i = 0; i < computation.size(); i++){ 
            if(computation[i]) delete computation[i]; 
        }

        computation = tmp_computation;
        throw;
    }
    
    for(int i = 0; i < tmp_computation.size(); i++){ 
        if(tmp_computation[i]) delete tmp_computation[i]; 
    }
}

PipelineDescriptor::~PipelineDescriptor(){
    for(int i = 0; i < computation.size(); i++){ 
        if(computation[i]) delete computation[i]; 
    }
}

PipeStepDescriptor& PipelineDescriptor::add_step(const PipeStepDescriptor& step){
    computation.push_back(step.clone());
    
    return *this;
}
    
// -----------------------------------------------------------------------------    
}
